using System.Windows;

namespace KlearTweet.Client.DataContainers
{
    public class ListWrapPanel : Controls.AnimatingPanelBase
    {
        private static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double ItemWidth
        {
            get { return (double)GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }
        private static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double ItemHeight
        {
            get { return (double)GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }
        private static readonly DependencyProperty HorizontalSpacingProperty = DependencyProperty.Register("HorizontalSpacing", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double HorizontalSpacing
        {
            get { return (double)GetValue(HorizontalSpacingProperty); }
            set { SetValue(HorizontalSpacingProperty, value); }
        }
        private static readonly DependencyProperty VerticalSpacingProperty = DependencyProperty.Register("VerticalSpacing", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double VerticalSpacing
        {
            get { return (double)GetValue(VerticalSpacingProperty); }
            set { SetValue(VerticalSpacingProperty, value); }
        }
        private static readonly DependencyProperty TopOffsetProperty = DependencyProperty.Register("TopOffset", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double TopOffset
        {
            get { return (double)GetValue(TopOffsetProperty); }
            set { SetValue(TopOffsetProperty, value); }
        }
        private static readonly DependencyProperty LeftOffsetProperty = DependencyProperty.Register("LeftOffset", typeof(double), typeof(ListWrapPanel), new PropertyMetadata(LayoutChanged));
        public double LeftOffset
        {
            get { return (double)GetValue(LeftOffsetProperty); }
            set { SetValue(LeftOffsetProperty, value); }
        }




        public int NumberOfColumnsFullyVisible
        {
            get
            {
                if (double.IsNaN(ItemWidth))
                    return 1;
                var itemsPerRow = (int)((_currentSize.Width - LeftOffset) / (ItemWidth + HorizontalSpacing));
                if (itemsPerRow < 1)
                    itemsPerRow = 1;
                return itemsPerRow;
            }
        }


        private bool _isFirstArrange; // TODO: "= true" causes a bug when the host wasn't visible yet at the first arrange
        private Size _currentSize;



        public ListWrapPanel()
        {
            HorizontalSpacing = TopOffset =
            VerticalSpacing = LeftOffset = 0;
            ItemWidth = double.NaN;
            ItemHeight = double.NaN;
            //AnimationCompleted += (s, e) => InvalidateMeasure(); // TODO: Why was this here?
        }


        private static void LayoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ListWrapPanel)d).InvalidateMeasure();
        }


        public System.Func<int, int> ChildIndex; // The order of the items can be defined outside of this panel
        public System.Func<Rect, bool> IsInsideViewport; // TODO: Should use an interface for these methods


        protected override Size MeasureOverride(Size availableSize)
        {
            var itemSize = new Size(
                double.IsNaN(ItemWidth)
                    ? (availableSize.Width - LeftOffset - HorizontalSpacing)
                    : ItemWidth,
                double.IsNaN(ItemHeight)
                    ? (availableSize.Height - TopOffset - VerticalSpacing)
                    : ItemHeight);

            foreach (var child in AllChildren)
                child.Measure(itemSize);

            // Compute needed width
            var width = availableSize.Width;
            if (double.IsInfinity(availableSize.Width))
            {
                if (double.IsNaN(ItemWidth))
                    width = 100; // TODO: Should warn but can't because while initializing, can be in this state (and throwing crashes Silverlight) // throw new System.Exception("Not supported; you must either set the ItemWidth or not put this panel in a ScrollViewer with allowed horizontal ScrollBar");
                else
                    width = (AllChildren.Count * (ItemWidth + HorizontalSpacing)) + LeftOffset;
            }

            // Compute needed height
            var itemsPerRow = double.IsNaN(ItemWidth) ? 1
                : (int)((width - LeftOffset) / (ItemWidth + HorizontalSpacing));
            if (itemsPerRow < 1)
                itemsPerRow = 1;
            var totalRows = System.Math.Ceiling(AllChildren.Count / (double)itemsPerRow);

            var height = availableSize.Height;
            if (double.IsInfinity(availableSize.Height))
            {
                if (double.IsNaN(ItemHeight))
                {
                    // Sum of children.DesiredSize.Height
                    height = TopOffset;
                    foreach (var child in AllChildren)
                        height += child.DesiredSize.Height + VerticalSpacing;
                }
                else
                    height = (totalRows * (ItemHeight + VerticalSpacing)) + TopOffset;
            }

            return new Size(width, height);
		}


        protected override Size ArrangeOverride(Size finalSize)
        {
            _currentSize = finalSize;
			var itemsPerRow = NumberOfColumnsFullyVisible;
			int rowIndex = 0;
			int colIndex = 0;

			double y = TopOffset;

            for (int i = 0; i < AllChildren.Count; i++)
			{
                var child = AllChildren[ChildIndex == null ? i : ChildIndex(i)];

				if (rowIndex >= itemsPerRow)
				{
					rowIndex = 0;
					colIndex++;
				}

                var finalItemWidth = double.IsNaN(ItemWidth)
                    ? (_currentSize.Width - LeftOffset - HorizontalSpacing)
                    : ItemWidth;
                var finalItemHeight = double.IsNaN(ItemHeight)
                    ? child.DesiredSize.Height
                    : ItemHeight;

                double x = (rowIndex * (finalItemWidth + HorizontalSpacing)) + LeftOffset;

                if (!double.IsNaN(ItemHeight))
                    y = (colIndex * (finalItemHeight + VerticalSpacing)) + TopOffset;

                var newRect = new Rect(x, y, finalItemWidth, finalItemHeight);
                SetElementLocation(child, newRect, !_isFirstArrange);

                rowIndex++;
                y += finalItemHeight + VerticalSpacing;
            }

            RefreshChildren();

            _isFirstArrange = false;
            return _currentSize;
        }


        private void RefreshChildren() // Simple virtualization
        {
            for (int i = 0; i < AllChildren.Count; i++)
            {
                var child = AllChildren[ChildIndex == null ? i : ChildIndex(i)];

                if (IsInsideViewport(GetElementTargetRect(child)))
                {
                    if (!Children.Contains(child))
                        Children.Add(child);
                }
                else
                {
                    if (Children.Contains(child))
                        Children.Remove(child);
                }
            }
        }

        public void Add(UIElement child)
        {
            AllChildren.Add(child);
        }

        public void Remove(UIElement child)
        {
            if (Children.Contains(child))
                Children.Remove(child);
            AllChildren.Remove(child);
        }


        public bool GetLocationOfChild(UIElement item, out int colIndex, out int rowIndex)
		{
			rowIndex = 0;
			colIndex = 0;
            if (item == null || !AllChildren.Contains(item))
				return false;

            for (int i = 0; i < AllChildren.Count; i++)
			{
                var child = AllChildren[ChildIndex == null ? i : ChildIndex(i)];

				if (rowIndex >= NumberOfColumnsFullyVisible)
				{
					rowIndex = 0;
					colIndex++;
				}

				if (item == child)
					break; // Found

				rowIndex++;
			}

			return true;
		}



        public UIElement GetChildAtLocation(int row, int col)
		{
			int rowIndex = 0;
			int colIndex = 0;

            for (int i = 0; i < AllChildren.Count; i++)
			{
                var child = AllChildren[ChildIndex == null ? i : ChildIndex(i)];

                if (rowIndex >= NumberOfColumnsFullyVisible)
				{
					rowIndex = 0;
					colIndex++;
				}

				if (row == colIndex && col == rowIndex)
					return child; // Found

				rowIndex++;
			}

			return null; // Not found
		}



        protected override Rect GetInitialRect(Size panelSize, Rect elementRect, UIElement element)
        {
            var location = new Point(0, panelSize.Height);
            return new Rect(location.X, location.Y, elementRect.Width, elementRect.Height);
        }
    }
}